﻿using System;
using System.Collections.Generic;
using StyleMVVM.DependencyInjection;

namespace StyleMVVM.Data.Collections
{
	[Export(typeof(IAttributeBag))]
	public class AttributeBag : ObservableDictionary<PropertyAttribute, object>, IAttributeBag
	{
		private WeakReference owner;

		public object Owner
		{
			get
			{
				if (owner != null)
				{
					return owner.Target;
				}

				return null;
			}
			set { owner = new WeakReference(value); }
		}

		public static PropertyAttribute RegisterAttribute<T>(string attributeName,
		                                                     Type owningtype,
		                                                     T defaultValue = default(T),
		                                                     Action<AttributeBag, PropertyAttributeChangedArgs> changeDelegate
			                                                     = null)
		{
			return new PropertyAttribute(attributeName, typeof(T), owningtype, defaultValue, changeDelegate);
		}

		public void Add(IAttributeBag attributes)
		{
			foreach (var attribute in attributes)
			{
				Add(attribute);
			}
		}

		public T GetValue<T>(PropertyAttribute attribute)
		{
			object returnValue = this[attribute];

			if (returnValue is T)
			{
				return (T)returnValue;
			}

			return default(T);
		}

		public void SetValue<T>(PropertyAttribute attribute, T value)
		{
			this[attribute] = value;
		}

		public override void Add(PropertyAttribute attribute, object value)
		{
			this[attribute] = value;
		}

		public override void Add(KeyValuePair<PropertyAttribute, object> item)
		{
			this[item.Key] = item.Value;
		}

		public override void Clear()
		{
			List<KeyValuePair<PropertyAttribute, object>> oldValues = new List<KeyValuePair<PropertyAttribute, object>>(this);

			// this is so we generate a change for someone listening rather than doing a 
			// hard Reset on the Collection
			foreach (PropertyAttribute propertyAttribute in Keys)
			{
				Remove(propertyAttribute);
			}

			foreach (var keyValuePair in oldValues)
			{
				var changeAction = keyValuePair.Key.AttributeChanged;

				if (changeAction != null)
				{
					changeAction(this, new PropertyAttributeChangedArgs(keyValuePair.Key, keyValuePair.Value, null));
				}
			}
		}

		public override bool Remove(PropertyAttribute attribute)
		{
			object oldValue;

			if (TryGetValue(attribute, out oldValue))
			{
				base.Remove(attribute);

				var changeAction = attribute.AttributeChanged;

				if (changeAction != null)
				{
					changeAction(this, new PropertyAttributeChangedArgs(attribute, oldValue, null));
				}
			}

			return false;
		}

		public override bool Remove(KeyValuePair<PropertyAttribute, object> item)
		{
			return Remove(item.Key);
		}

		public override object this[PropertyAttribute attribute]
		{
			get
			{
				object returnValue;

				if (!TryGetValue(attribute, out returnValue))
				{
					returnValue = attribute.DefaultValue;
				}

				return returnValue;
			}
			set
			{
				object oldValue;

				TryGetValue(attribute, out oldValue);

				base[attribute] = value;

				var changeAction = attribute.AttributeChanged;

				if (changeAction != null)
				{
					changeAction(this, new PropertyAttributeChangedArgs(attribute, oldValue, value));
				}
			}
		}
	}
}